<template>
  <el-select v-model="selectedIcon" :placeholder="placeholder" :disabled="disabled" :clearable="clearable" filterable
    :filter-method="filterIcons" :popper-class="'custom-selector-popper'" v-model:visible="selectVisible"
    @change="handleChange" @visible-change="handleVisibleChange">
    <!-- 使用 el-option 包装以确保与 el-select 兼容 -->
    <el-option value="" disabled style="padding: 0;" class="eloption">
      <!-- 使用 Tabs 组件替代分类标签 -->
      <el-tabs v-if="categories.length > 1" v-model="activeCategory" @tab-change="handleTabChange"
        class="icon-selector-tabs">
        <el-tab-pane v-for="category in categories" :key="category.name" :name="category.name"
          :label="`${category.label} (${category.icons.length})`">
          <!-- 图标网格 -->
          <el-scrollbar height="200px" @scroll="handleScroll">
            <div class="icon-grid-content">
              <div class="icon-grid">
                <div v-for="icon in filteredIcons" :key="icon" class="icon-item" @click.stop="selectIcon(icon)">
                  <el-icon :size="iconSize">
                    <component :is="getIconComponent(icon)" :iconName="icon" />
                  </el-icon>
                  <span v-if="showIconName" class="icon-name">{{ icon }}</span>
                </div>
              </div>
              <!-- 加载更多指示器 -->
              <div v-if="hasMore" class="load-more-container">
                <div v-if="isLoading" class="loading-indicator">加载中...</div>
                <div v-else class="load-more-hint">向下滚动加载更多</div>
              </div>
            </div>
          </el-scrollbar>
        </el-tab-pane>
      </el-tabs>

      <!-- 单分类或搜索结果 -->
      <el-scrollbar v-else height="200px" @scroll="handleScroll">
        <div class="icon-grid-content">
          <div class="icon-grid">
            <div v-for="icon in filteredIcons" :key="icon" class="icon-item" @click.stop="selectIcon(icon)">
              <el-icon :size="iconSize">
                <component :is="getIconComponent(icon)" :iconName="icon" />
              </el-icon>
              <span v-if="showIconName" class="icon-name">{{ icon }}</span>
            </div>
          </div>
          <!-- 加载更多指示器 -->
          <div v-if="hasMore" class="load-more-container">
            <div v-if="isLoading" class="loading-indicator">加载中...</div>
            <div v-else class="load-more-hint">向下滚动加载更多</div>
          </div>
        </div>
      </el-scrollbar>

      <!-- 空状态 -->
      <el-empty v-if="filteredIcons.length === 0" :description="emptyText" />
    </el-option>

    <!-- 自定义前缀模板 -->
    <template #prefix v-if="selectedIcon && showSelectedIcon">
      <el-icon :size="16">
        <component :is="getIconComponent(selectedIcon)" :iconName="selectedIcon" />
      </el-icon>
    </template>
  </el-select>
</template>

<script setup lang="ts">
/**
 * IconSelector 组件 - 高性能图标选择器
 * 
 * 功能特性：
 * - 支持 Element Plus 图标库
 * - 支持阿里巴巴矢量图标库
 * - 搜索过滤功能
 * - 分页懒加载
 * - 自定义分类
 * - 性能优化（缓存、节流、防抖）
 * 
 * @author IconSelector Team
 * @version 2.0.0
 */
import { ref, computed, watch, defineAsyncComponent, h, nextTick, onMounted, onUnmounted } from 'vue'
import { ElSelect, ElOption, ElIcon, ElEmpty, ElTabs, ElTabPane, ElScrollbar } from 'element-plus'
import { getElementPlusIcons, isElementPlusIcon } from '../utils/icons'

// ==================== 类型定义 ====================

/**
 * 图标分类接口
 */
interface IconCategory {
  name: string    // 分类唯一标识
  label: string   // 分类显示名称
  icons: string[] // 图标名称列表
}

/**
 * 组件属性接口
 */
interface Props {
  // 基础属性
  modelValue?: string           // v-model 绑定值
  placeholder?: string          // 占位符文本
  emptyText?: string           // 空状态提示文本
  iconSize?: number            // 图标大小
  clearable?: boolean          // 是否可清除
  showSelectedIcon?: boolean   // 是否显示已选图标
  showIconName?: boolean       // 是否显示图标名称
  disabled?: boolean           // 是否禁用
  
  // 自定义配置
  categories?: IconCategory[]   // 自定义分类
  customIcons?: string[]       // 自定义图标列表
  iconPrefix?: string          // 图标前缀
  
  // 阿里图标库配置
  iconFontUrl?: string         // 阿里图标库 JS 文件 URL
  iconFontPrefix?: string      // 阿里图标前缀
  iconFontIcons?: string[]     // 阿里图标名称列表
}

const props = withDefaults(defineProps<Props>(), {
  placeholder: '请选择图标',
  emptyText: '暂无匹配的图标',
  iconSize: 16,
  categories: () => [],
  customIcons: () => [],
  iconPrefix: 'icon',
  clearable: true,
  showSelectedIcon: true,
  showIconName: false,
  disabled: false,
  // 阿里图标库默认配置
  iconFontUrl: '',
  iconFontPrefix: 'icon-',
  iconFontIcons: () => []
})
/**
 * 组件事件接口
 */
interface Emits {
  'update:modelValue': [value: string | undefined]  // v-model 更新事件
  'change': [value: string | undefined]             // 值变化事件
  'category-change': [category: string]             // 分类切换事件
  'load-error': [error: Error]                      // 加载错误事件
  'load-success': [url: string]                     // 加载成功事件
}

const emit = defineEmits<Emits>()

// ==================== 响应式数据 ====================

// 基础状态
const selectedIcon = ref(props.modelValue)  // 当前选中的图标
const activeCategory = ref('')              // 当前激活的分类
const searchQuery = ref('')                 // 搜索关键词
const selectVisible = ref(false)            // 下拉框是否可见

// ==================== 性能优化相关 ====================

const iconComponentCache = new Map()        // 图标组件缓存
const pageSize = 30                         // 每页显示的图标数量（减少以提升性能）
const currentPage = ref(1)                  // 当前页码
const isLoading = ref(false)                // 加载状态
const maxCacheSize = 200                    // 最大缓存数量，防止内存泄漏

// ==================== 阿里图标库相关状态 ====================

const iconFontLoaded = ref(false)           // 阿里图标库是否已加载
const iconFontLoading = ref(false)          // 阿里图标库是否正在加载
const loadedIconFontUrls = new Set()        // 已加载的图标库URL集合

// ==================== 阿里图标库加载函数 ====================

/**
 * 动态加载阿里图标库
 * @param url 图标库 JS 文件 URL
 * @returns 加载是否成功
 */
const loadIconFont = async (url: string): Promise<boolean> => {
  if (!url || loadedIconFontUrls.has(url)) {
    return true
  }

  if (iconFontLoading.value) {
    return false
  }

  iconFontLoading.value = true

  try {
    // 检查是否已存在相同的script标签
    const existingScript = document.querySelector(`script[src="${url}"]`)
    if (existingScript) {
      loadedIconFontUrls.add(url)
      iconFontLoaded.value = true
      iconFontLoading.value = false
      emit('load-success', url)
      return true
    }

    // 创建script标签动态加载
    const script = document.createElement('script')
    script.src = url.startsWith('//') ? `https:${url}` : url
    script.async = true

    return new Promise((resolve) => {
      script.onload = () => {
        loadedIconFontUrls.add(url)
        iconFontLoaded.value = true
        iconFontLoading.value = false
        console.log(`阿里图标库加载成功: ${url}`)
        emit('load-success', url)
        resolve(true)
      }

      script.onerror = () => {
        iconFontLoading.value = false
        const error = new Error(`阿里图标库加载失败: ${url}`)
        console.error(error.message)
        emit('load-error', error)
        resolve(false)
      }

      document.head.appendChild(script)
    })
  } catch (error) {
    iconFontLoading.value = false
    const loadError = error instanceof Error ? error : new Error('阿里图标库加载异常')
    console.error('阿里图标库加载异常:', loadError)
    emit('load-error', loadError)
    return false
  }
}

/**
 * 检查是否为阿里图标
 * @param iconName 图标名称
 * @returns 是否为阿里图标
 */
const isIconFontIcon = (iconName: string): boolean => {
  return iconName.startsWith(props.iconFontPrefix)
}

// ==================== 计算属性 ====================

/**
 * 所有图标分类（包括 Element Plus、阿里图标库、自定义图标）
 */
const allCategories = computed<IconCategory[]>(() => {
  if (props.categories.length > 0) {
    return props.categories
  }

  // 默认使用 Element Plus 图标
  const elementPlusIcons = getElementPlusIcons()

  // 自定义图标分类
  const customIconsCategory: IconCategory[] = props.customIcons.length > 0 ? [{
    name: 'custom',
    label: '自定义图标',
    icons: props.customIcons
  }] : []

  // 阿里图标库分类
  const iconFontCategory: IconCategory[] = props.iconFontIcons.length > 0 ? [{
    name: 'iconfont',
    label: '阿里图标',
    icons: props.iconFontIcons
  }] : []

  return [
    ...elementPlusIcons,
    ...iconFontCategory,
    ...customIconsCategory
  ]
})

/**
 * 当前显示的分类（搜索模式或正常分类）
 */
const categories = computed(() => {
  if (searchQuery.value.trim()) {
    // 搜索模式下显示所有匹配的图标
    const allIcons = allCategories.value.flatMap(cat => cat.icons)
    const filteredIcons = allIcons.filter(icon =>
      icon.toLowerCase().includes(searchQuery.value.toLowerCase())
    )
    return [{
      name: 'search',
      label: '搜索结果',
      icons: filteredIcons
    }]
  }
  return allCategories.value
})

/**
 * 当前激活的分类对象
 */
const currentCategory = computed(() => {
  if (!activeCategory.value && categories.value.length > 0) {
    activeCategory.value = categories.value[0].name
  }
  return categories.value.find(cat => cat.name === activeCategory.value) || categories.value[0]
})

/**
 * 当前页面显示的图标列表（支持分页懒加载）
 */
const filteredIcons = computed(() => {
  const icons = currentCategory.value?.icons || []
  // 分页加载优化：累积加载而不是分页
  const endIndex = currentPage.value * pageSize
  return icons.slice(0, endIndex)
})

// 总页数
const totalPages = computed(() => {
  const icons = currentCategory.value?.icons || []
  return Math.ceil(icons.length / pageSize)
})

// 是否还有更多图标
const hasMore = computed(() => {
  return currentPage.value < totalPages.value
})

// ==================== 核心方法 ====================

/**
 * 获取图标组件（支持缓存和多种图标类型）
 * @param iconName 图标名称
 * @returns Vue 组件
 */
const getIconComponent = (iconName: string) => {
  // 参数验证
  if (!iconName || typeof iconName !== 'string') {
    console.warn('IconSelector: 无效的图标名称', iconName)
    return null
  }

  // 检查缓存
  if (iconComponentCache.has(iconName)) {
    return iconComponentCache.get(iconName)
  }

  let component
  if (isElementPlusIcon(iconName)) {
    // Element Plus 图标
    component = defineAsyncComponent({
      loader: async () => {
        try {
          const iconModule = await import('@element-plus/icons-vue')
          const IconComponent = (iconModule as any)[iconName]
          if (IconComponent) {
            return IconComponent
          }
          // 返回一个默认的占位符组件，使用render函数
          return {
            name: 'FallbackIcon',
            props: ['iconName'],
            render() {
              return h('span', {
                class: 'fallback-icon'
              }, this.iconName ? this.iconName.charAt(0) : '?')
            }
          }
        } catch (error) {
          const loadError = error instanceof Error ? error : new Error(`图标加载失败: ${iconName}`)
          console.warn(`Failed to load icon "${iconName}":`, loadError)
          emit('load-error', loadError)
          // 返回一个默认的占位符组件，使用render函数
          return {
            name: 'FallbackIcon',
            props: ['iconName'],
            render() {
              return h('span', {
                class: 'fallback-icon'
              }, this.iconName ? this.iconName.charAt(0) : '?')
            }
          }
        }
      },
      errorComponent: {
        name: 'ErrorIcon',
        render() {
          return h('span', {
            class: 'fallback-icon'
          }, '?')
        }
      },
      loadingComponent: {
        name: 'LoadingIcon',
        render() {
          return h('span', {
            class: 'fallback-icon'
          }, '...')
        }
      }
    })
  } else if (isIconFontIcon(iconName)) {
    // 阿里图标库图标
    component = {
      name: 'IconFontIcon',
      props: ['iconName'],
      render() {
        return h('svg', {
          class: 'icon-font-svg',
          'aria-hidden': true
        }, [
          h('use', {
            'xlink:href': `#${this.iconName}`
          })
        ])
      }
    }
  } else {
    // 对于其他自定义图标，返回一个简单的占位符组件，使用render函数
    component = {
      name: 'CustomIcon',
      props: ['iconName'],
      render() {
        return h('span', {
          class: 'fallback-icon'
        }, this.iconName ? this.iconName.charAt(0) : '?')
      }
    }
  }

  // 缓存组件（带LRU机制）
  if (iconComponentCache.size >= maxCacheSize) {
    // 删除最早的缓存项
    const firstKey = iconComponentCache.keys().next().value
    iconComponentCache.delete(firstKey)
  }
  iconComponentCache.set(iconName, component)
  return component
}

// 加载更多图标（优化性能）
const loadMoreIcons = async () => {
  if (isLoading.value || !hasMore.value) return

  isLoading.value = true
  // 使用 requestAnimationFrame 优化渲染性能
  await nextTick()
  requestAnimationFrame(() => {
    currentPage.value++
    isLoading.value = false
  })
}

// 重置分页
const resetPagination = () => {
  currentPage.value = 1
  isLoading.value = false
}

const handleTabChange = (categoryName: string | number) => {
  activeCategory.value = String(categoryName)
  resetPagination() // 切换分类时重置分页
  emit('category-change', String(categoryName))
}

/**
 * 选择图标
 * @param iconName 图标名称
 */
const selectIcon = (iconName: string) => {
  if (!iconName || typeof iconName !== 'string') {
    console.warn('IconSelector: 尝试选择无效的图标', iconName)
    return
  }

  selectedIcon.value = iconName
  selectVisible.value = false // 关闭下拉框
  emit('update:modelValue', iconName)
  emit('change', iconName)
}

// 防抖搜索函数（优化性能）
let searchTimeout: number | null = null
const filterIcons = (query: string) => {
  // 清除之前的定时器
  if (searchTimeout) {
    clearTimeout(searchTimeout)
  }

  // 设置新的防抖定时器，减少延迟提升响应性
  searchTimeout = setTimeout(() => {
    searchQuery.value = query
    resetPagination() // 搜索时重置分页
  }, 200) // 200ms 防抖延迟（减少延迟）
}

const handleChange = (value: string | undefined) => {
  selectedIcon.value = value
  emit('update:modelValue', value)
  emit('change', value)
}

const handleVisibleChange = (visible: boolean) => {
  selectVisible.value = visible
  if (!visible) {
    searchQuery.value = ''
    resetPagination() // 关闭时重置分页
  } else {
    // 打开时预加载第一页
    nextTick(() => {
      resetPagination()
    })
  }
}

// 节流滚动事件处理（优化性能）
let scrollTimeout: number | null = null
let lastScrollTime = 0
const handleScroll = (scrollInfo: { scrollTop: number; scrollLeft: number }) => {
  const now = Date.now()
  // 使用时间戳节流，更精确的控制
  if (now - lastScrollTime < 50) return // 50ms 节流
  
  lastScrollTime = now
  
  // 节流处理，避免频繁触发
  if (scrollTimeout) return

  scrollTimeout = setTimeout(() => {
    // el-scrollbar 提供 scrollInfo 对象，包含 scrollTop 等信息
    // 需要获取滚动容器的实际尺寸来计算是否到达底部
    const scrollContainer = document.querySelector('.el-scrollbar__wrap')
    if (scrollContainer) {
      const { scrollTop } = scrollInfo
      const { scrollHeight, clientHeight } = scrollContainer

      // 当滚动到底部附近时加载更多（增加预加载距离）
      if (scrollHeight - scrollTop - clientHeight < 150) {
        loadMoreIcons()
      }
    }

    scrollTimeout = null
  }, 50) // 50ms 节流延迟（减少延迟）
}

// 监听器
watch(() => props.modelValue, (newValue) => {
  selectedIcon.value = newValue
}, { immediate: true })

// 监听搜索查询变化，优化重新渲染
watch(searchQuery, () => {
  resetPagination()
}, { flush: 'post' })

// 监听分类变化，清理缓存中不需要的组件
watch(activeCategory, (newCategory, oldCategory) => {
  if (oldCategory && newCategory !== oldCategory) {
    // 清理不常用的缓存以释放内存
    if (iconComponentCache.size > maxCacheSize * 0.8) {
      const keysToDelete = Array.from(iconComponentCache.keys()).slice(0, Math.floor(maxCacheSize * 0.3))
      keysToDelete.forEach(key => iconComponentCache.delete(key))
    }
  }
})

// 组件挂载时加载阿里图标库
onMounted(async () => {
  if (props.iconFontUrl) {
    await loadIconFont(props.iconFontUrl)
  }
})

// 监听iconFontUrl变化，动态加载图标库
watch(() => props.iconFontUrl, async (newUrl) => {
  if (newUrl) {
    await loadIconFont(newUrl)
  }
}, { immediate: false })

// 组件卸载时清理定时器
const cleanup = () => {
  if (searchTimeout) {
    clearTimeout(searchTimeout)
    searchTimeout = null
  }
  if (scrollTimeout) {
    clearTimeout(scrollTimeout)
    scrollTimeout = null
  }
}

// 组件卸载时清理
onUnmounted(cleanup)
</script>

<style scoped lang="scss">
// SASS 变量定义
$popper-width: 400px;
$dropdown-height: 500px;
$grid-gap: 8px;
$icon-item-min-height: 30px;
$icon-item-border-radius: 5px;
$transition-duration: 0.15s;
$loading-spinner-size: 16px;
$loading-spinner-border: 2px;
$fallback-icon-size: 20px;
$icon-name-font-size: 10px;
$load-more-padding: 16px;
$load-more-margin-top: 8px;

// SASS 混合 (Mixins)
@mixin flex-center {
  display: flex;
  align-items: center;
  justify-content: center;
}

@mixin transition-smooth($property: all) {
  transition: #{$property} $transition-duration ease;
}

@mixin loading-spinner {
  content: '';
  width: $loading-spinner-size;
  height: $loading-spinner-size;
  border: $loading-spinner-border solid var(--el-color-primary);
  border-top: $loading-spinner-border solid transparent;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

.custom-selector-popper {
  width: $popper-width;
  height: 500px;

  // 深度选择器样式
  :deep(.el-select-dropdown__wrap) {
    max-height: $dropdown-height;
  }

  .el-select-dropdown__item {
    height: auto !important;
    padding: 0 !important;
  }
}

// 主要组件样式
.icon-selector-tabs {
  width: 400px;
  height: 100%;

  :deep(.el-tabs__content) {
    padding: 0;
    height: 100%;
  }
}

.icon-grid-content {
  padding: 8px 16px;
}

.icon-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(50px, 1fr));
  gap: $grid-gap;
}

.icon-item {
  @include flex-center;
  flex-direction: column;
  min-height: $icon-item-min-height;
  border: 1px solid var(--el-border-color);
  border-radius: $icon-item-border-radius;
  cursor: pointer;
  @include transition-smooth(background-color);
  will-change: background-color;

  &:hover {
    background-color: #eaeefe;
    color: #2E5CF6;
    border-color: #2E5CF6;
  }
}

// 加载更多相关样式
.load-more-container {
  @include flex-center;
  padding: $load-more-padding;
  margin-top: $load-more-margin-top;
}

.loading-indicator {
  color: var(--el-color-primary);
  font-size: 14px;
  @include flex-center;
  gap: $grid-gap;

  &::before {
    @include loading-spinner;
  }
}

.load-more-hint {
  color: var(--el-text-color-secondary);
  font-size: 12px;
}

// 动画定义
@keyframes spin {
  0% {
    transform: rotate(0deg);
  }

  100% {
    transform: rotate(360deg);
  }
}

// 图标名称样式
.icon-name {
  font-size: $icon-name-font-size;
  color: var(--el-text-color-regular);
  text-align: center;
  margin-top: 4px;
  word-break: break-all;
  line-height: 1.2;
}

// 回退图标样式
.fallback-icon {
  @include flex-center;
  width: $fallback-icon-size;
  height: $fallback-icon-size;
  background-color: var(--el-color-primary);
  color: white;
  border-radius: 50%;
  font-size: 16px;
  font-weight: bold;
}

// 阿里图标库样式
.icon-font-svg {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}
</style>